package participants;

import java.util.ArrayList;

import main.ResOTCModel;

import org.resotc.model.Centroid;
import org.resotc.model.Element;
import org.resotc.model.Individual;
import org.resotc.model.IndividualType;

import sim.engine.SimState;
import sim.util.Double2D;
import statistics.Reporting;
import statistics.util.SessionFactoryUtil;

public class IndividualImpl implements Individual {

	// Who is it?
	protected String myID;

	// Specifications of individual
	protected Element currentElement;
	protected Centroid myDestination;
	protected Centroid myStart;
	protected double traveltime;
	// Characteristics
	private double desiredSpeed;

	// Position information
	protected double x;
	protected double y;
	protected double length;
	protected double relativeLinkPosition;

	// Statistics
	protected double simStart;
	protected int myStops;

	// Status
	private boolean isNextElementChosen;
	private Element chosenNextElement;

	public IndividualType individualType;
	private int laneId;

	public IndividualImpl(String SSID, double x, double y,
			double beginSimulationTime, IndividualType individualType,
			Centroid myStart) {
		super();
		this.x = x;
		this.y = y;
		this.simStart = beginSimulationTime;
		myStops = 0;
		desiredSpeed = 1;
		relativeLinkPosition = 0;
		isNextElementChosen = false;
		chosenNextElement = null;
		myID = SSID;
		this.individualType = individualType;
		this.length = this.getLength();
		this.myStart = myStart;
	}
	
	public void setLaneId(int laneId) {
		this.laneId = laneId;
	}
	
	public int getLaneId() {
		return laneId;
	}

	public double getX() {
		return x;
	}

	public void writeStatistic() {
		// with constructor-call, session is created
		SessionFactoryUtil sfu = new SessionFactoryUtil();

		// start session
		sfu.beginTransaction();

		Reporting reporting = new Reporting();

		reporting.setIndividualId(this.getMyID());
		reporting.setStops(myStops);
		reporting.setDestcentroid(myDestination.getId());
		reporting.setStartcentroid(myStart.getId());

		// reporting.setTravelTime(traveltime);
		System.out.println("hash " + this.hashCode());
		System.out.println("ID " + this.getMyID());
		System.out.println("stop " + myStops);
		System.out.println("dest " + myDestination.getId());
		System.out.println("myStart" + myStart.getId());
		// save event
		sfu.saveTransaction(reporting);
		sfu.commitTransaction();
		sfu.listReports();
		sfu.closeSession();
	}

	public void setX(double x) {
		this.x = x;
	}

	public double getY() {
		return y;
	}

	public void setY(double y) {
		this.y = y;
	}

	public void setLength(double length) {
		this.length = length;
	}

	public double getLength() {
		return length;
	}

	public Double2D getPosition() {
		return new Double2D(x, y);
	}

	public Element getCurrentNetworkElement() {
		return currentElement;
	}

	public double getDesiredSpeed() {
		return desiredSpeed;
	}

	public void transferToNextElement() {
		isNextElementChosen = false;
		chosenNextElement = null;
	}

	public Element chooseNextElement(ArrayList<Element> nextElements) {
		if (nextElements == null || nextElements.size() == 0) {
			System.err
					.println("[INDIVIDUAL] ERROR: Cannot choose from list of possible next elements - invalid arguments!");
			return null;
		}

		// If next step is already selected - used that, otherwise choose
		// (needed since intersections have to probably ask more than once...)
		if (isNextElementChosen) {
			return chosenNextElement;
		} else {
			this.isNextElementChosen = true;
			return selectElement(nextElements);
		}
	}

	protected Element selectElement(ArrayList<Element> nextElements) {
		return null;
	}

	public double calculateDistanceInStep() {
		return 0;
	}

	public double calculateTravelTime(double endTime) {
		return endTime - simStart;
	}

	public int getNumberOfStops() {
		return myStops;
	}

	public void setNumberOfStops(int stops) {
		this.myStops = stops;
	}

	public String getMyID() {
		return myID;
	}

	public void setTravelTime(double time) {
		this.traveltime = time;
	}

	public void setMyID(String myID) {
		this.myID = myID;
	}

	public Centroid getMyDestination() {
		return myDestination;
	}

	public void setMyDestination(Centroid myDestination) {
		this.myDestination = myDestination;
	}

	public Centroid getMyStart() {
		return myStart;
	}

	public void setMyStart(Centroid myStart) {
		this.myStart = myStart;
	}

	public void setDesiredSpeed(double value) {
		desiredSpeed = value;
	}

	public double getRelativeLinkPosition() {
		return relativeLinkPosition;
	}

	public void resetRelativeLinkPosition() {
		this.relativeLinkPosition = 0;
	}

	public void setRelativeLinkPosition(double relativeLinkPosition,
			Double2D Pre, Double2D Suc) {

		double delta = relativeLinkPosition
				/ (Math.sqrt((Pre.x - Suc.x) * (Pre.x - Suc.x)
						+ (Pre.y - Suc.y) * (Pre.y - Suc.y)));
		double rx = Pre.x + (delta * (Suc.x - Pre.x));
		double ry = Pre.y + (delta * (Suc.y - Pre.y));

		this.setX(rx);
		this.setY(ry);

		this.relativeLinkPosition = relativeLinkPosition;
	}

	public Element getNextChosenElement() {
		return chosenNextElement;
	}

	public IndividualType getIndividualType() {
		return individualType;
	}

	public void updatePosition(SimState state) {
		ResOTCModel resOTCModel = (ResOTCModel) state;
		Double2D newLoc = new Double2D(this.x, this.y);
		resOTCModel.cars.setObjectLocation(this, newLoc);
	}

	public void updatePosition(ArrayList<Element> elemente, Double2D location) {
		// TODO Auto-generated method stub

	}
}
